<!DOCTYPE html>
<html>
<head>
    <!-- 引入 CodeMirror 核心文件和 JSON 模式 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/javascript/javascript.min.js"></script>
    
    <!-- XLSX 库 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
    
    <style>
        .container { padding: 20px; font-family: Arial; }
        .CodeMirror { 
            border: 1px solid #ddd; 
            height: 400px !important; 
            font-family: 'Fira Code', monospace;
        }
        button { 
            padding: 10px 20px; 
            background: #4CAF50; 
            color: white; 
            border: none; 
            cursor: pointer; 
            margin: 10px 5px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h3>JSON 转 Excel 转换器（专业编辑器版）</h3>
        <textarea id="jsonEditor"></textarea>
        <br>
        <button onclick="handleConvert()">转换为 Excel</button>
        <button onclick="jsonFormat()">格式化数据</button>
        <button onclick="clearEditor()">清空</button>
    </div>

<script>
// 处理 JSON 转换的核心逻辑
function handleConvert() {
    try {
        const jsonInput = editor.getValue();
        if (!jsonInput.trim()) {
            alert("请输入JSON数据");
            return;
        }
        // 关键修复：将 undefined 替换为 null（JSON 不支持 undefined）
        const sanitizedInput = jsonInput.replace(/undefined/g, 'null');
        const jsonData = JSON.parse(sanitizedInput);
        if (jsonData.length === 0) {
            alert("JSON数据不能为空数组");
            return;
        }
        // 修改部分：检查用户是否取消输入
        const fileName = prompt("文件名（默认：data.xlsx）", "data.xlsx");
        if (fileName === null) { // 用户点击取消
            return; // 直接终止流程
        }
        const finalFileName = fileName.trim() || 'data.xlsx'; // 处理空输入
        jsonToExcel(jsonData, finalFileName);
    } catch (error) {
        alert(`转换失败：${error.message}`);
    }
}

/**
 * 将 JSON 数据转换为 Excel 文件
 * @param {Array} jsonData - 需要转换的 JSON 数组
 * @param {string} fileName - 生成的 Excel 文件名
 */
function jsonToExcel(jsonData, fileName = 'data.xlsx') {
    if (!Array.isArray(jsonData)) {
        throw new Error('输入必须是数组');
    }

    // 创建新工作簿和工作表
    const wb = XLSX.utils.book_new();
    const wsData = convertJsonToSheetData(jsonData); // 转换数据为表格格式
    const ws = XLSX.utils.aoa_to_sheet(wsData);     // 将数组转换为工作表
    
    // 添加工作表并保存文件
    XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
    XLSX.writeFile(wb, fileName);
}

/**
 * 将 JSON 数据转换为工作表数据（二维数组）
 * @param {Array} jsonData - 输入的 JSON 数据
 * @returns {Array} - 工作表数据（包含表头和行数据）
 */
function convertJsonToSheetData(jsonData) {
    // 判断是否是对象数组（例如 [{name: "A"}, {name: "B"}]）
    const isObjectArray = jsonData.every(item => 
        typeof item === 'object' && !Array.isArray(item) && item !== null
    );

    let headers = [], rows = [];

    if (isObjectArray) {
        // 处理对象数组：提取所有键作为表头
        const allKeys = jsonData.reduce((keys, item) => {
            Object.keys(item).forEach(key => keys.add(key));
            return keys;
        }, new Set());
        headers = Array.from(allKeys);

        // 按表头顺序提取值
        rows = jsonData.map(item => 
            headers.map(key => sanitizeValue(item[key]))
        );
    } else {
        // 处理普通数组（例如 [[1,2], [3,4]] 或 ["A", "B"]）
        // 计算最大列数以填充不足的单元格
        const maxColumns = jsonData.reduce((max, item) => 
            Math.max(max, Array.isArray(item) ? item.length : 1), 0
        );
        
        // 生成默认列名（列 1, 列 2...）
        headers = Array.from({length: maxColumns}, (_, i) => `列 ${i+1}`);
        
        // 转换数据并补全空值
        rows = jsonData.map(item => {
            const arr = Array.isArray(item) ? [...item] : [item];
            while (arr.length < maxColumns) arr.push(null); // 填充空列
            return arr.map(sanitizeValue);
        });
    }

    return [headers, ...rows]; // 组合表头和数据行
}

/**
 * 清理无效值（将 undefined/null/空字符串转为 null）
 * @param {*} value - 原始值
 * @returns {*} - 处理后的值
 */
function sanitizeValue(value) {
    return (value === null || value === undefined || value === '') ? null : value;
}

// 清空文本区域内容
function clearEditor() {
    editor.setValue('');
}

// json校验功能
function jsonFormat(){
    // 获取输入的 JSON 字符串
    let jsonInput = editor.getValue();
    if (jsonInput.length == 0) {
        alert("请输入JSON数据");
        return;
    }

    try {
        // 尝试解析JSON
        const jsonData = JSON.parse(jsonInput);
        // 格式化JSON并重新填入
        const formattedJSON = JSON.stringify(jsonData, null, 2);
        editor.setValue(formattedJSON);
    } catch (error) {
        // 提取错误位置
        const positionMatch = error.message.match(/position (\d+)/);
        if (positionMatch) {
            const errPos = parseInt(positionMatch[1], 10);
            // 计算错误行号
            const errSubstring = jsonInput.substring(0, errPos);
            const lineNumber = errSubstring.split('\n').length;
            // 获取错误行内容
            const lines = jsonInput.split('\n');
            const errorLine = lines[lineNumber - 1] || '';
            alert(`JSON格式错误 第${lineNumber}行: ${error.message}\n错误内容: ${errorLine}`);
        } else {
            alert(`JSON格式错误: ${error.message}`);
        }
    }
}

// 初始化 CodeMirror 编辑器
const editor = CodeMirror.fromTextArea(document.getElementById('jsonEditor'), {
    mode: 'javascript',  // JSON 模式
    lineNumbers: true,   // 显示行号
    tabSize: 2,         // 缩进2空格
    theme: 'default',    // 主题
    gutters: ["CodeMirror-linenumbers", "CodeMirror-lint-markers"],
    lint: true,          // 开启语法检查
    autoCloseBrackets: true, // 自动闭合括号
    matchBrackets: true  // 高亮匹配括号
});

</script>
</body>
</html>